package com.yociyy.auth.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yociyy.auth.service.IAuthTokenService;
import com.yociyy.common.constants.Constants;
import com.yociyy.redis.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.ServletWebRequest;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 用户认证业务实现
 * 
 * @author: YoCiyy
 * @date: 2020/7/6
 */
@Service
@RequiredArgsConstructor
public class AuthTokenServiceImpl implements IAuthTokenService {

	private final ClientDetailsService clientDetailsService;

	private final AuthenticationManager authenticationManager;

	private final AuthorizationServerTokenServices authorizationServerTokenServices;

	private final ObjectMapper objectMapper;

	private final RedisUtils redis;

	private final static String ACCESS_TOKEN = Constants.ACCESS_TOKEN;

	private final static String ACCESS_USERID = Constants.ACCESS_USERID;

	private final static long EXPIRE = 12 * 60 * 60;

	@Override
	public Map<String, Object> authenticationByForm(String username, String password, ServletWebRequest webRequest) throws IOException {

		String header = webRequest.getHeader("Authorization");

		if (header == null || !header.startsWith("Basic ")) {
			throw new UnapprovedClientAuthenticationException("请求头中无client信息");
		}

		String[] clientInfos = extractAndDecodeHeader(header, webRequest.getRequest());

		String clientId = clientInfos[0];
		String clientSecret = clientInfos[1];

		ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);

		if (clientDetails == null) {
			throw new UnapprovedClientAuthenticationException("clientId对应的配置信息不存在：" + clientId);
		} else if (!StringUtils.equals(clientDetails.getClientSecret(), clientSecret)) {
			throw new UnapprovedClientAuthenticationException("clientSecret不匹配:" + clientId);
		}

		TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP, clientId, clientDetails.getScope(), "custom");

		OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);

		Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
		SecurityContextHolder.getContext().setAuthentication(authentication);

		OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
		OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);


		Map<String,Object> map = new HashMap<>(3);
		map.put("userId", 1L);
		map.put("token", token.getValue());
		map.put("expire", 12 * 60 * 60);

//		// 存储用户信息至redis
//		redis.set(ACCESS_TOKEN + token.getValue(), new BaseUser().setId(1L).setAccount("YoCiyy"), EXPIRE);
//		// 存储token至redis
//		redis.set(ACCESS_USERID + 1L, token, EXPIRE);

//		webRequest.getResponse().setContentType("application/json;charset=UTF-8");
//		webRequest.getResponse().getWriter().write(objectMapper.writeValueAsString(token));

		return map;

	}

	private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException {

		byte[] base64Token = header.substring(6).getBytes("UTF-8");
		byte[] decoded;
		try {
			decoded = Base64.decode(base64Token);
		} catch (IllegalArgumentException e) {
			throw new BadCredentialsException("Failed to decode basic authentication token");
		}

		String token = new String(decoded, "UTF-8");

		int delim = token.indexOf(":");

		if (delim == -1) {
			throw new BadCredentialsException("Invalid basic authentication token");
		}
		return new String[] { token.substring(0, delim), token.substring(delim + 1) };
	}
}
